From: Sebastian Keller Date: Wed, 30 Apr 2025 11:13:41 +0000 (+0200) Subject: timeLimitsManager: Don't adjust times for offset changes after resume X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~124^2^2~102 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=de1be54007507dba6a5aeda727d680a69684f701;p=gnome-shell.git timeLimitsManager: Don't adjust times for offset changes after resume The non-continuous change in wall clock time after a suspend/resume triggers timeChangeNotify which adjusts the stored time data according to the difference in clock offsets. The monotonic clock however might not have advanced during the suspend. Since the offsets are the difference between the wall clock time and monotonic time, the difference between the old and new offset will be the duration of the sleep if there was no change in the monotonic clock. Adjusting the time data by this will move the beginning of the inactive period from the suspend forward in time to just before the resume, effectively removing it. Since all previous times are adjusted as well, this will result in time data as if the system had been running continuously, but was started later than it actually was. Fix this by not adjusting times on resume and just updating the current offset to account for the monotonic clock not advancing during sleep. Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/8289 Part-of: --- diff --git a/js/misc/timeLimitsManager.js b/js/misc/timeLimitsManager.js index 294175398..dc4590584 100644 --- a/js/misc/timeLimitsManager.js +++ b/js/misc/timeLimitsManager.js @@ -175,6 +175,7 @@ export const TimeLimitsManager = GObject.registerClass({ this._timerId = 0; this._timeChangeId = 0; this._clockOffsetSecs = 0; + this._ignoreClockOffsetChanges = false; // Start tracking timings this._updateSettings(); @@ -231,6 +232,9 @@ export const TimeLimitsManager = GObject.registerClass({ this._clockOffsetSecs = this._getClockOffset(); try { this._timeChangeId = this._clock.timeChangeNotify(() => { + if (this._ignoreClockOffsetChanges) + return GLib.SOURCE_CONTINUE; + const newClockOffsetSecs = this._getClockOffset(); const oldClockOffsetSecs = this._clockOffsetSecs; @@ -348,9 +352,14 @@ export const TimeLimitsManager = GObject.registerClass({ } async _onPrepareForSleep(preparingForSleep) { - // Just come back from sleep, so take another inhibitor. - if (!preparingForSleep) + // Just come back from sleep, so take another inhibitor. Also update + // the clock offset to account for the monotonic clock not advancing + // during sleep. + if (!preparingForSleep) { this._ensureInhibitor(); + this._clockOffsetSecs = this._getClockOffset(); + this._ignoreClockOffsetChanges = false; + } try { await this._updateUserState(true); @@ -358,9 +367,14 @@ export const TimeLimitsManager = GObject.registerClass({ console.warn(`Failed to update user state: ${e.message}`); } - // Release the inhibitor if we’re preparing to sleep. - if (preparingForSleep) + // Release the inhibitor if we’re preparing to sleep. Also avoid + // adjusting the time due to offset changes caused by the monotonic + // clock not advancing during sleep until we have updated the current + // offset to account for this. + if (preparingForSleep) { + this._ignoreClockOffsetChanges = true; this._releaseInhibitor(); + } } /** Shut down the state machine and write out the state file. */ diff --git a/tests/unit/timeLimitsManager.js b/tests/unit/timeLimitsManager.js index 3eba0aa7c..bcbd77e88 100644 --- a/tests/unit/timeLimitsManager.js +++ b/tests/unit/timeLimitsManager.js @@ -282,6 +282,12 @@ class TestHarness { time: TestHarness.timeStrToSecs(timeStr), newPreparingForSleepState: true, }); + this._insertEvent({ + type: 'time-change', + time: TestHarness.timeStrToSecs(timeStr) + 1, + newTime: TestHarness.timeStrToSecs(timeStr) + duration - 1, + callback: null, + }); this._insertEvent({ type: 'preparing-for-sleep-state-change', time: TestHarness.timeStrToSecs(timeStr) + duration, @@ -1309,6 +1315,19 @@ describe('Time limits manager', () => { 'dailyLimitTime': TestHarness.timeStrToSecs('2024-06-01T17:00:00Z'), }); + // Ensure the suspend event caused a state change to inactive and its + // time was not adjusted due to offset changes on resume + harness.addAssertionEvent('2024-06-01T15:00:02Z', () => { + const [, historyContents] = harness.mockHistoryFile.load_contents(null); + expect(JSON.parse(new TextDecoder().decode(historyContents))) + .withContext('History file contents') + .toContain({ + 'oldState': UserState.ACTIVE, + 'newState': UserState.INACTIVE, + 'wallTimeSecs': TestHarness.timeStrToSecs('2024-06-01T12:00:01Z'), + }); + }); + harness.shutdownManager('2024-06-01T15:20:00Z', timeLimitsManager); harness.run();